# this is meant to be invoked via jenkins and assumes that jenkins has
# cloned/updated the source

# arguments, specified via "-D"
#   -DVERBOSITY=<0-5>
#   -DDASHSUBMIT=<TRUE|FALSE>    # mostly for debugging to avoid cdash submission
#   -DCMAKE_ARGS_LIST="-DVAR1=VAL1;-DVAR2=VAL2;..."  # arguments to pass directly to cmake
#   -DCDASHVER=<version of cdash>  # should be either 3.1 or not set
#   -DRXR_APPEND_TAGS="tags as used by run_xyce_regression script to add"
#   -DMPI_TESTING=<TRUE|FALSE>

cmake_minimum_required(VERSION 3.23)

# verbosity level
#   0 - no specific screen output (default)
#   5 - all screen output available
if(NOT VERBOSITY)
  set(VERBOSITY 0)
endif()

# default TRUE
if(NOT DEFINED DASHSUBMIT)
  set(DASHSUBMIT TRUE)
endif()

# default FALSE
if(NOT DEFINED MPI_TESTING)
  set(MPI_TESTING FALSE)
endif()

# the version of cdash matters for the custom Test.xml file that is
# generated
if(NOT DEFINED CDASHVER)
  set(CDASHVER 0.0)
endif()

if($ENV{XYCE_MPI})
  if(MPI_TESTING)
    set(xyceSitePostFix "PbPr")
  else()
    set(xyceSitePostFix "PbSr")
  endif()
else()
  set(xyceSitePostFix "")
endif()
    

# error check
if(NOT DEFINED ENV{MYBUILDNAME})
  message(FATAL_ERROR "ERROR: Required environment varialble \"MYBUILDNAME\" not set")
endif()
if(NOT DEFINED ENV{branch})
  message(FATAL_ERROR "ERROR: Required environment varialble \"branch\" not set")
endif()
if(NOT DEFINED ENV{TESTSET})
  message(FATAL_ERROR "ERROR: Required environment variable \"TESTSET\" not set")
endif()

# function to execute the "run_xyce_regression" script
#    parameters:
#       1 - tag string as required by run_xyce_regression
#       2 - name of the results file to be created by run_xyce_regression
#       3 - the name of the command for the script to run. usually
#           either the binary itself, Xyce, or the mpirun command with
#           the binary
function(RUNXYCEREGRESSION my_taglist my_regr_res_fname my_xyce_cmd)

  message("executing ${XYCE_REGR_SCRIPT}...")
  if(VERBOSITY GREATER 1)
    message("[VERB2]: my_taglist = ${my_taglist}")
    message("[VERB2]: my_xyce_cmd: ${my_xyce_cmd}")
    message("[VERB2]: my_regr_res_fname: ${my_regr_res_fname}")
  endif()

  execute_process(COMMAND ${XYCE_REGR_SCRIPT}
    --output=${CTEST_BINARY_DIRECTORY}/Xyce_Regression
    --xyce_test=$ENV{WORKSPACE}/tests/Xyce_Regression
    --xyce_verify=$ENV{WORKSPACE}/tests/Xyce_Regression/TestScripts/xyce_verify.pl
    --ignoreparsewarnings
    --taglist=${my_taglist}
    --resultfile=${my_regr_res_fname}
    ${my_xyce_cmd}
    OUTPUT_VARIABLE regrOut
    ERROR_VARIABLE regrOut
    RESULT_VARIABLE xyce_reg_result)

  if(VERBOSITY GREATER 3)
    message("[VERB4]: exit status of regression script ${XYCE_REGR_SCRIPT}: ${xyce_reg_result}")
    message("[VERB4]: using xyce command: ${my_xyce_cmd}")
    message("[VERB4]: screen output from regression script ${XYCE_REGR_SCRIPT}: ${regrOut}")
  endif()

endfunction()

# function to list the contents of a specified subdirectory
#    parameters:
#       1 - full spec for directory to list
#       2 - variable in parent into which the subdirectory name as
#           specified in the TAG file, will be set
function(GETTESTSUBDIR basedirname result)

  # read the TAG file to get the current test subdirectory name
  file(READ ${basedirname}/TAG tagFileContent)
  string(REGEX REPLACE "\n" ";" tagFileContent ${tagFileContent})

  list(GET tagFileContent 0 dirname)
  if(VERBOSITY GREATER 2)
    message("[VERB3]: directory name \"${dirname}\"")
  endif()

  # set the result in the caller. note that the value of result,
  # $result, is the name of the variable in the call
  set(${result} ${dirname} PARENT_SCOPE)
endfunction()

# function to read the custom xyce test results XML file and convert it
# to a format consistent with ctest for a unified submission. at
# present this just replaces the "BuildStamp" in the custom test
# results XML file with the one generated by ctest
#    parameters:
#       1 - name of the custom xyce results file
#       2 - name of the subdirectory which ctest generated. this
#           corresponds to the BuildStamp
#       3 - the test track as used by ctest, one of Experimental,
#           Nightly, Weekly or Continuous
#       4 - the output file name, without the .xml extension which is
#           always added and a directory should not be part of this
#           name
function(CONVERTTESTXML inputfn subdirname track outfname)
  file(STRINGS ${inputfn} lines_list)
  foreach(fline ${lines_list})
    if(${fline} MATCHES "BuildStamp=\"(.*)\"")
      string(REGEX REPLACE "(.*BuildStamp=)\"(.*)\""  "\\1\"${subdirname}-${track}\"" outfline ${fline})
      list(APPEND new_line_list ${outfline})
    else()
      list(APPEND new_line_list ${fline})
    endif()
  endforeach()

  # convert the cmake list to a unified string with new lines
  string(REPLACE ";" "\n" out_contents "${new_line_list}")
  file(WRITE "${CTEST_BINARY_DIRECTORY}/Testing/${subdirname}/${outfname}.xml" ${out_contents})
endfunction()

# function to execute a Xyce executable with the "-capability" option in
# order to obtain a list of capabilities subsequently used when
# executign the run_xyce_regression script
#    parameters:
#       1 - the full path to the Xyce binary to run
function(GET_XYCE_CAPABILITIES xyce_exe)

  # execute Xyce with the "-capabilities" option
  execute_process(COMMAND ${xyce_exe} -capabilities
    RESULT_VARIABLE res_ret
    OUTPUT_VARIABLE term_cap_out
    ERROR_VARIABLE err_out)

  # if the execution fails
  if(NOT ${res_ret} EQUAL 0)
    message("ERROR: when querying Xyce capabilities. Error output:")
    message(FATAL_ERROR "${term_cap_out}")
  endif()

  # build up the tag list according to the output of the query. each
  # of the following is making a correspondence between a line output
  # by "Xyce -capabilities" and a tag to use when invoking
  # run_xyce_regression.
  string(FIND "${term_cap_out}" "Parallel with MPI" res_var)
  if(${res_var} EQUAL -1)
    set(myTagList "+serial?klu")
  else()
    set(myTagList "+parallel")
  endif()
  if("$ENV{TESTSET}" STREQUAL "Weekly"
      OR "$ENV{TESTSET}" STREQUAL "QA"
      OR "$ENV{TESTSET}" STREQUAL "FINAL")

    set(myTagList "${myTagList}?weekly?nightly")
  else()
    set(myTagList "${myTagList}+nightly")
  endif()

  string(FIND "${term_cap_out}" "Verbose" res_var)
  if(${res_var} EQUAL -1)
    set(myTagList "${myTagList}-verbose?noverbose")
  else()
    set(myTagList "${myTagList}?verbose-noverbose")
  endif()

  string(FIND "${term_cap_out}" "Non-Free device models" res_var)
  if(NOT ${res_var} EQUAL -1)
    set(myTagList "${myTagList}?nonfree")
  else()
    set(myTagList "${myTagList}-nonfree")
  endif()

  string(FIND "${term_cap_out}" "Radiation models" res_var)
  if(NOT ${res_var} EQUAL -1)
    set(myTagList "${myTagList}?rad")
    string(FIND "${term_cap_out}" "Reaction parser" res_var)
    if(NOT ${res_var} EQUAL -1)
      set(myTagList "${myTagList}?qaspr")
    endif()
  else()
    set(myTagList "${myTagList}-rad")
  endif()

  string(FIND "${term_cap_out}" "ATHENA" res_var)
  if(NOT ${res_var} EQUAL -1)
    set(myTagList "${myTagList}?athena")
  endif()

  string(FIND "${term_cap_out}" "FFT" res_var)
  if(NOT ${res_var} EQUAL -1)
    set(myTagList "${myTagList}?fft")
  endif()

  string(FIND "${term_cap_out}" "C++14" res_var)
  if(NOT ${res_var} EQUAL -1)
    set(myTagList "${myTagList}?cxx14")
  endif()

  string(FIND "${term_cap_out}" "Stokhos enabled" res_var)
  if(NOT ${res_var} EQUAL -1)
    set(myTagList "${myTagList}?stokhos")
  endif()

  string(FIND "${term_cap_out}" "ROL enabled" res_var)
  if(NOT ${res_var} EQUAL -1)
    set(myTagList "${myTagList}?rol")
  endif()

  string(REGEX MATCH "Amesos2.*Basker.*enabled" out_var "${term_cap_out}")
  if(out_var)
    set(myTagList "${myTagList}?amesos2basker")
  endif()

  string(REGEX MATCH "Amesos2.*KLU2.*enabled" out_var "${term_cap_out}")
  if(out_var)
    set(myTagList "${myTagList}?amesos2klu")
  endif()

  find_program(XDMBDLEXE NAMES xdm_bdl)
  if(NOT ${XDMBDLEXE} STREQUAL "XDMBDLEXE-NOTFOUND")
    set(myTagList "${myTagList}?xdm")
  endif()

  if(NOT "${RXR_APPEND_TAGS}" STREQUAL "")
    set(myTagList "${myTagList}${RXR_APPEND_TAGS}")
  endif()

  # set parent TAGLIST from function-local variable
  set(TAGLIST "${myTagList}" PARENT_SCOPE)

endfunction()

# WORKSPACE is an environment variable set by jenkins
set(CTEST_SOURCE_DIRECTORY "$ENV{WORKSPACE}/source/Xyce")

# the specified directory must exist or ctest will error out
set(CTEST_BINARY_DIRECTORY "$ENV{WORKSPACE}/build")

# this should probably be a variable in the environment or passed in,
# but for now it's hard-coded to 1am
set(CTEST_NIGHTLY_START_TIME "01:00:00 MDT")

# use the "hostname" command as the CTEST_SITE variable, which is used
# in the "Site" column on the dashboard
find_program(HNAME NAMES hostname)
execute_process(COMMAND "${HNAME}"
  OUTPUT_VARIABLE CTEST_SITE
  OUTPUT_STRIP_TRAILING_WHITESPACE)

# add any postfix to the site
set(CTEST_SITE "${CTEST_SITE} ${xyceSitePostFix}")

# find the custom xyce regression testing script
find_program(XYCE_REGR_SCRIPT run_xyce_regression
  HINTS $ENV{WORKSPACE}/tests/Xyce_Regression/TestScripts
  REQUIRED)

# find the custom perl script to create the results XML file. note
# that different versions of cdash can require slight different
# formats for the XML files.
if(${CDASHVER} EQUAL 3.1)
  find_program(XYCE_CDASH_GEN summary-dart-nosubmit.cdash-v3p1.pl
    HINTS $ENV{WORKSPACE}/Scripts/reporting
    REQUIRED)
else()
  find_program(XYCE_CDASH_GEN summary-dart-nosubmit.pl
    HINTS $ENV{WORKSPACE}/Scripts/reporting
    REQUIRED)
endif()

# this is used as the "Build Name" column on the dashboard
set(CTEST_BUILD_NAME "$ENV{MYBUILDNAME}")

# used for invocation of parallel make
if(DEFINED ENV{NUM_JOBS})
  set(CTEST_BUILD_FLAGS "-j$ENV{NUM_JOBS}")
else()
  set(CTEST_BUILD_FLAGS "-j8")
endif()

# note that "Weekly" is just a Nightly category with a different group
# name
if(NOT DEFINED ENV{TESTSET})
  message(FATAL_ERROR "ERROR: You must set the environment variable TESTSET to one of Nighlty, Weekly or Experimental")
endif()

if($ENV{TESTSET} STREQUAL "Nightly")
  set(MODEL "Nightly")
  set(TESTGROUP "Nightly")
elseif($ENV{TESTSET} STREQUAL "Weekly")
  set(MODEL "Nightly")
  set(TESTGROUP "Weekly")
else()
  set(MODEL "Experimental")
  set(TESTGROUP "Experimental")
endif()

set(CTEST_PROJECT_NAME "Xyce")

set(CTEST_DROP_METHOD "https")
set(CTEST_DROP_SITE "xyce-cdash.sandia.gov")
set(CTEST_DROP_LOCATION "/submit.php?project=Xyce")


if(VERBOSITY GREATER 4)
  message("[VERB5]: CMAKE_ARGS_LIST = ${CMAKE_ARGS_LIST}")
endif()
set(XYCE_CMAKE_CONF_ARG "")
foreach(cmakeopt IN LISTS CMAKE_ARGS_LIST)
  set(XYCE_CMAKE_CONF_ARG "${XYCE_CMAKE_CONF_ARG} ${cmakeopt}")
endforeach()
if(VERBOSITY GREATER 4)
  message("[VERB5]: XYCE_CMAKE_CONF_ARG = ${XYCE_CMAKE_CONF_ARG}")
endif()

# generate the cmake configuration command
find_program(CTEST_CMAKE_COMMAND cmake REQUIRED)
set(CTEST_CMAKE_GENERATOR "Unix Makefiles")
set(CTEST_CONFIGURE_COMMAND "${CTEST_CMAKE_COMMAND} \"-GUnix Makefiles\"")
set(CTEST_CONFIGURE_COMMAND "${CTEST_CONFIGURE_COMMAND} ${XYCE_CMAKE_CONF_ARG}")
set(CTEST_CONFIGURE_COMMAND "${CTEST_CONFIGURE_COMMAND} ${CTEST_SOURCE_DIRECTORY}")

if(VERBOSITY GREATER 1)
  message("[VERB1]: CTEST_CONFIGURE_COMMAND = ${CTEST_CONFIGURE_COMMAND}")
endif()

# begin ctest procedures. MODEL should be one of Nighlty, Weekly,
# Continuous or Experimental. this can use custom categories via the
# GROUP option to ctest_start() if desired
ctest_start(${MODEL} GROUP ${TESTGROUP})

# this runs cmake on xyce
ctest_configure()

# this runs make
ctest_build()

# generate the taglist for the regression testing script
GET_XYCE_CAPABILITIES(${CTEST_BINARY_DIRECTORY}/src/Xyce)

# determine if this was a parallel build, if that is the case testing
# actually consists of two phases, 1) testing with "mpirun" and then
# 2) running serial tests that don't have the "nompi" tag directly
# with the "Xyce" executable
string(FIND ${TAGLIST} "+parallel" doMpiTesting)

if(doMpiTesting LESS 0 AND MPI_TESTING)
  message(FATAL_ERROR "ERROR: Requested MPI testing but the Xyce binary was not built with MPI support")
endif()

if(MPI_TESTING)

  # if this is an openmpi build and mpi testing was requested run the
  # regression testing script for such tests
  message("performing OpenMPI testing...")

  # some tests don't work with mpi
  set(TAGLIST "${TAGLIST}?hb?ac?mpde-klu")

  set(MPICMD "mpirun -np 2 --bind-to none ${CTEST_BINARY_DIRECTORY}/src/Xyce")

  if(VERBOSITY GREATER 1)
    message("[VERB2]: TAGLIST (parallel)=\"${TAGLIST}\"")
  endif()

  RUNXYCEREGRESSION(${TAGLIST} "${CTEST_BINARY_DIRECTORY}/PbPr_regr_test_results_all" ${MPICMD})
  list(APPEND regFileNames "${CTEST_BINARY_DIRECTORY}/PbPr_regr_test_results_all")

else()

  # if this is a parallel build but serial run, set things accordingly
  string(REPLACE "+parallel" "+serial-nompi" TAGLIST ${TAGLIST})

  RUNXYCEREGRESSION(${TAGLIST} "${CTEST_BINARY_DIRECTORY}/regr_test_results_all"
    ${CTEST_BINARY_DIRECTORY}/src/Xyce)
  
  list(APPEND regFileNames "${CTEST_BINARY_DIRECTORY}/regr_test_results_all")

endif()

# run the perl script to summarize results for submission to the dashboard
if(VERBOSITY GREATER 1)
  message("[VERB2]: XYCE_CDASH_GEN = ${XYCE_CDASH_GEN}")
  message("[VERB2]:   CTEST_SITE = ${CTEST_SITE}")
  message("[VERB2]:   MYBUILDNAME = $ENV{MYBUILDNAME}")
  message("[VERB2]:   branch = $ENV{branch}")
  message("[VERB2]:   regression results file names = ${regFileNames}")
  message("[VERB2]:  site postfix = ${xyceSitePostFix}")
  message("[VERB2]:   TESTSET = $ENV{TESTSET}")
endif()

# combine multiple results files into a single file
foreach(fname IN LISTS regFileNames)

  # read in the results file and convert it to an XML format suitable
  # for submission to cdash
  message("executing custom xyce regression report script, ${XYCE_CDASH_GEN} for ${fname}")
  execute_process(COMMAND ${XYCE_CDASH_GEN}
    ${CTEST_SITE}
    $ENV{MYBUILDNAME}
    $ENV{branch}
    ${fname}
    $ENV{TESTSET}
    OUTPUT_VARIABLE submitOut
    ERROR_VARIABLE submitOut
    RESULT_VARIABLE xyce_cdash_gen_result)
  
  if(VERBOSITY GREATER 3)
    message("[VERB4]: exit status of script ${XYCE_CDASH_GEN}: ${xyce_cdash_gen_result}")
    message("[VERB4]: screen output from script ${XYCE_CDASH_GEN}: ${submitOut}")
  endif()

  # figure out the directory for the dashboard submission and copy the
  # custom results file into it
  GETTESTSUBDIR(${CTEST_BINARY_DIRECTORY}/Testing testSubDirName)
  if(VERBOSITY GREATER 4)
    message("[VERB5]: Active \"Testing\" subdirectory name: ${testSubDirName}")
  endif()
  if(VERBOSITY GREATER 4)
    message("[VERB5]: Using \"${testSubDirName}\" for test results")
  endif()

  # this will convert the "BuildStamp" generated and written to the
  # XML file by the custom xyce perl script to the same generated by
  # ctest. this will make the configure, build, and test results show
  # up on the same line in cdash.
  CONVERTTESTXML(${fname}.xml
    ${testSubDirName}
    $ENV{TESTSET}
    "Test")

endforeach()

# submit results to the dashboard
if(DASHSUBMIT)
  ctest_submit(RETRY_COUNT 10 RETRY_DELAY 30)
endif()
